﻿using MyK3CloudCore.IOC.Attributes;
using MyK3CloudCore.IOC.Cache;
using MyK3CloudCore.IOC.Extensions;
using MyK3CloudCore.IOC.Types;
using SQLBuilder.Enums;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Threading.Tasks;

namespace MyK3CloudCore.IOC.K3Client
{
    public class K3Queryable<T> : IK3Queryable<T> where T : class
    {
        private Expression<Func<T, bool>> Filter { get; set; }
        private Expression<Func<T, object>> OrderExpression { get; set; }
        private OrderType[]? OrderTypes { get; set; }
        private readonly int topRowCount = 0;
        private int startRow = 0;
        private int limit = 0;
        /// <summary>
        /// K3Cloud表单查询链式请求
        /// </summary>
        public K3Queryable() : this(null)
        {
        }
        /// <summary>
        /// K3Cloud表单查询链式请求
        /// </summary>
        /// <param name="whereExpression">where筛选条件</param>
        public K3Queryable(Expression<Func<T, bool>>? whereExpression)
        {
            Filter = Filter.AndFilter(whereExpression);
        }

        private bool IsCache { get; set; }
        private string CacheKey { get; set; } = string.Empty;
        private int CacheDurationInSeconds { get; set; }
        public IK3Queryable<T> WithCache(string cacheKey, int cacheDurationInSeconds = int.MaxValue)
        {
            CacheKey = cacheKey;
            CacheDurationInSeconds = cacheDurationInSeconds;
            IsCache = true;
            return this;
        }
        public IK3Queryable<T> WithCache(int cacheDurationInSeconds = int.MaxValue)
        {
            CacheDurationInSeconds = cacheDurationInSeconds;
            IsCache = true;
            return this;
        }
        public List<T> ToList()
        {
            return ToListAsync().GetAwaiter().GetResult();
        }
        public async Task<List<T>> ToListAsync()
        {
            if (IsCache)
            {
                var tmpKey = string.IsNullOrEmpty(CacheKey) ? $"WHERE_{ExpressionBuilder.GetFilterString(this.Filter)}_{ExpressionBuilder.GetOrderString(this.OrderExpression, OrderTypes)}_{this.startRow}_{this.limit}" : CacheKey;
                var cache = MemoryCacheHelper<List<T>>.GetInstance().Get(tmpKey);
                if (cache == null)
                {
                    var rst = await K3Scoped.Client.ExecuteBillQueryAsync(Filter, OrderExpression, OrderTypes, topRowCount, startRow, limit);
                    MemoryCacheHelper<List<T>>.GetInstance().Add(tmpKey, rst, CacheDurationInSeconds);
                    return rst;
                }
                return cache;
            }
            return await K3Scoped.Client.ExecuteBillQueryAsync(Filter, OrderExpression, OrderTypes, topRowCount, startRow, limit);
        }
        public async Task<List<T>> ToPageListAsync(int pageNumber, int pageSize)
        {
            if (pageNumber <= 0)
            {
                startRow = 0;
            }
            else
            {
                startRow = (pageNumber - 1) * pageSize;
            }
            if (pageSize <= 0)
            {
                pageSize = 10;
            }
            limit = pageSize;
            return await ToListAsync();
        }
        public async Task<List<T>> ToPageListAsync(int pageNumber, int pageSize, Refasync<int> totalNumber)
        {
            totalNumber.Value = await CountAsync();
            return await ToPageListAsync(pageNumber, pageSize);
        }
        public async Task<List<T>> ToPageListAsync(int pageNumber, int pageSize, Expression<Func<T, object>> selector, Refasync<int> totalNumber)
        {
            totalNumber.Value = await CountAsync(selector);
            return await ToPageListAsync(pageNumber, pageSize);
        }
        public async Task<List<T>> ToPageListAsync(int pageNumber, int pageSize, string countField, Refasync<int> totalNumber)
        {
            totalNumber.Value = await CountAsync(countField);
            return await ToPageListAsync(pageNumber, pageSize);
        }
        public async Task<List<T>> ToPageListAsync(int pageNumber, int pageSize, Refasync<int> totalNumber, Refasync<int> totalPage)
        {
            totalNumber.Value = await CountAsync();
            totalPage.Value = (totalNumber.Value + pageSize - 1) / pageSize;
            return await ToPageListAsync(pageNumber, pageSize);
        }
        public async Task<int> CountAsync()
        {
            var fieldName = typeof(T).GetProperties()
                .Where(t => t.GetCustomAttributes(typeof(ignoreAttribute), true).Length == 0 &&
                (t.PropertyType == typeof(string) ||
                t.PropertyType == typeof(int) ||
                t.PropertyType == typeof(int?) ||
                t.PropertyType == typeof(float) ||
                t.PropertyType == typeof(float?) ||
                t.PropertyType == typeof(double) ||
                t.PropertyType == typeof(double?) ||
                t.PropertyType == typeof(decimal) ||
                t.PropertyType == typeof(decimal?))
            ).Select(t => t.Name).LastOrDefault();
            if (string.IsNullOrEmpty(fieldName))
            {
                throw new ArgumentNullException(nameof(fieldName));
            }
            return await CountAsync(fieldName);
        }
        public async Task<int> CountAsync(Expression<Func<T, object>> selector)
        {
            if (selector.Body is NewExpression newExpression)
            {
                var firstName = newExpression.Members.Cast<PropertyInfo>().Select(t => t.Name).FirstOrDefault();
                if (string.IsNullOrEmpty(firstName)) { throw new ArgumentException("计数字段为何如此复杂,用法参考：t=>t.FMaterialName"); }
                return await CountAsync(firstName);
            }
            MemberExpression? memberSelectorExpression = selector.Body as MemberExpression ?? ((UnaryExpression)selector.Body).Operand as MemberExpression;
            if (memberSelectorExpression != null)
            {
                var property = memberSelectorExpression.Member as PropertyInfo;
                if (property != null)
                {
                    return await CountAsync(property.Name);
                }
            }
            throw new ArgumentException("计数字段为何如此复杂,用法参考：t=>t.FMaterialName");
        }
        public async Task<int> CountAsync(string countField)
        {
            if (IsCache && !string.IsNullOrEmpty(countField))
            {
                var tmpKey = string.IsNullOrEmpty(CacheKey) ? $"{countField}_{ExpressionBuilder.GetFilterString(this.Filter)}_{this.startRow}_{this.limit}" : CacheKey + countField;
                var cache = MemoryCacheHelper<int?>.GetInstance().Get(tmpKey);
                if (cache == null)
                {
                    var rst = await K3Scoped.Client.GetTotalCountAsync(Filter, countField);
                    MemoryCacheHelper<int?>.GetInstance().Add(tmpKey, rst, CacheDurationInSeconds);
                    return rst;
                }
                return (int)cache;
            }
            return await K3Scoped.Client.GetTotalCountAsync(Filter, countField);
        }
        public async Task<Dictionary<string, decimal>?> SumAsync(params string[] sumFiled)
        {
            if (IsCache && sumFiled.Length != 0)
            {
                var tmpKey = string.IsNullOrEmpty(CacheKey) ? $"{string.Join('_', sumFiled)}_{ExpressionBuilder.GetFilterString(this.Filter)}_{this.startRow}_{this.limit}" : CacheKey + string.Join('_', sumFiled);
                var cache = MemoryCacheHelper<Dictionary<string, decimal>>.GetInstance().Get(tmpKey);
                if (cache == null)
                {
                    var rst = await K3Scoped.Client.GetSumAsync(Filter, sumFiled);
                    MemoryCacheHelper<Dictionary<string, decimal>>.GetInstance().Add(tmpKey, rst, CacheDurationInSeconds);
                    return rst;
                }
                return cache;
            }
            return await K3Scoped.Client.GetSumAsync(Filter, sumFiled);
        }
        public async Task<Dictionary<string, decimal>?> SumAsync(Expression<Func<T, object>> selector)
        {
            if (selector.Body is NewExpression memberExpression)
            {
                var arrayNames = memberExpression.Members.Cast<PropertyInfo>()
                    .Where(t => t.PropertyType == typeof(int) ||
                        t.PropertyType == typeof(int?) ||
                        t.PropertyType == typeof(short) ||
                        t.PropertyType == typeof(short?) ||
                        t.PropertyType == typeof(long) ||
                        t.PropertyType == typeof(long?) ||
                        t.PropertyType == typeof(float) ||
                        t.PropertyType == typeof(float?) ||
                        t.PropertyType == typeof(double) ||
                        t.PropertyType == typeof(double?) ||
                        t.PropertyType == typeof(decimal) ||
                        t.PropertyType == typeof(decimal?)
                ).Select(t => t.Name).ToArray();
                if (arrayNames.Length == 0) { throw new ArgumentException("不能对非数字字段进行sum运算"); }
                return await SumAsync(arrayNames);
            }
            else
            {
                MemberExpression memberSelectorExpression = (selector.Body as MemberExpression ?? ((UnaryExpression)selector.Body).Operand as MemberExpression) ?? throw new ArgumentException("参数错误:请参考 t=>t.FQty 或 t=> new {t.FQty,t.FBaseQty}");
                var property = memberSelectorExpression.Member as PropertyInfo;
                if (property != null)
                {
                    if (property.PropertyType == typeof(int) ||
                        property.PropertyType == typeof(int?) ||
                        property.PropertyType == typeof(float) ||
                        property.PropertyType == typeof(float?) ||
                        property.PropertyType == typeof(double) ||
                        property.PropertyType == typeof(double?) ||
                        property.PropertyType == typeof(decimal) ||
                        property.PropertyType == typeof(decimal?))
                    {
                        return await SumAsync(property.Name);
                    }
                    else
                    {
                        throw new ArgumentException("不能对非数字字段进行sum运算");
                    }
                }
                return default;
            }
        }
        public IK3Queryable<T> Where(Expression<Func<T, bool>> expression)
        {
            if (expression == null) return this;
            Filter = Filter.AndFilter(expression);
            return this;
        }
        public IK3Queryable<T> WhereIF(bool isWhere, Expression<Func<T, bool>> expression)
        {
            return isWhere ? Where(expression) : this;
        }
        public IK3Queryable<T> OrderBy(Expression<Func<T, object>> expression, OrderType[]? orders = default)
        {
            OrderExpression = expression;
            OrderTypes = orders;
            return this;
        }
        public IK3Queryable<T> Skip(int count)
        {
            startRow = count;
            return this;
        }
        public IK3Queryable<T> Take(int count)
        {
            limit = count;
            return this;
        }
        public IK3Queryable<T> Clone()
        {
            return (IK3Queryable<T>)this.MemberwiseClone();
        }
        public async Task<T> FirstAsync()
        {
            limit = 1;
            var queryList = await ToListAsync();
            return queryList.FirstOrDefault();
        }
    }
}
namespace MyK3CloudCore.IOC.Types
{
    public class Refasync<T>
    {
        public Refasync() { }
        public Refasync(T? value) { Value = value; }
        public T? Value { get; set; }
        public override string ToString()
        {
            T? value = Value;
            return value == null ? "" : value.ToString();
        }
        public static implicit operator T?(Refasync<T?> r) { return r.Value; }
        public static implicit operator Refasync<T>(T value) { return new Refasync<T>(value); }
    }
}